#include "RenzoVertexBufferGL.h"
#include "RenzoScene.h"
#include "RenzoCamera.h"
#include "RenzoTextureManager.h"

namespace Renzo
{
#define BUFFER_OFFSET(i) ((char *)NULL + (i))

VertexBufferGL::VertexBufferGL(void)
: vbo(0), ibo(0)
{
	// create OpenGL VBO object
	glGenBuffersARB(1, &vbo);

	// create OpenGL "IBO" (index buffer object)
	glGenBuffersARB(1, &ibo);
}

VertexBufferGL::~VertexBufferGL(void)
{
}

void VertexBufferGL::copyToHardware() {
	if (state == UNTOUCHED) return;

	/*
	float verts[] = {
		0.0f, 0.0f, 0.0f,
		10.0f, 0.0f, 0.0f,
		0.0f, 0.0f, 10.0f
	};
	int idx[] = {0, 1, 2};
	*/

	/*
	float* verts = new float[vertices.size() * 3];
	for (int i = 0; i < vertices.size(); ++i) {
		verts[3*i + 0] = vertices[i].position.x;
		verts[3*i + 1] = vertices[i].position.y;
		verts[3*i + 2] = vertices[i].position.z;
	}

	unsigned int* idx = new unsigned int[indices.size()];
	for (int i = 0; i < indices.size(); ++i) {
		idx[i] = indices[i];
	}
	*/
	
	/*
	for (int i = 0; i < nbTriangles; ++i) {
		glBegin(GL_TRIANGLES);
		//glBegin(GL_POINTS);
			glVertex3f(verts[idx[3*i]  *3+0], verts[idx[3*i]  *3+1], verts[idx[3*i]  *3+2]);
			glVertex3f(verts[idx[3*i+1]*3+0], verts[idx[3*i+1]*3+1], verts[idx[3*i+1]*3+2]);
			glVertex3f(verts[idx[3*i+2]*3+0], verts[idx[3*i+2]*3+1], verts[idx[3*i+2]*3+2]);
		glEnd();
	}*/

	//glFrontFace(GL_CW);

	
	
	

	glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo);

	// copy vertices data to VBO
	glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertices.size() * sizeof(Vertex), &(vertices[0].position.x), GL_STREAM_DRAW_ARB); // stream + draw (change every frame and use once every frame)
	//glBufferDataARB(GL_ARRAY_BUFFER_ARB, 3 * 3 * sizeof(float), verts, GL_STREAM_DRAW_ARB);
	//glBufferDataARB(GL_ARRAY_BUFFER_ARB, vertices.size() * (3 * sizeof(float)), verts, GL_STREAM_DRAW_ARB);

	
	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ibo);

	// copy indices data to VBO
	glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indices.size() * sizeof(Index), &(indices[0]), GL_STATIC_DRAW_ARB); // static + draw (fixed and used once every frame)
	//glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 3 * sizeof(int), idx, GL_STATIC_DRAW_ARB);
	//glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indices.size() * sizeof(unsigned int), idx, GL_STATIC_DRAW_ARB);
	
	//delete[] verts;
	//delete[] idx;

	// reset state
	state = UNTOUCHED;
}

// from Prof. Low's Bump Mapping assignment, CS4345
void DrawAxes( float length )
{	
    glPushAttrib( GL_ALL_ATTRIB_BITS );
    glDisable( GL_LIGHTING );
	glDisable( GL_TEXTURE_2D );
    glLineWidth( 3.0 );
    glBegin( GL_LINES );
        // x-axis.
        glColor3f( 1.0, 0.0, 0.0 );
        glVertex3d( 0.0, 0.0, 0.0 );
        glVertex3d( length, 0.0, 0.0 );
        // y-axis.
        glColor3f( 0.0, 1.0, 0.0 );
        glVertex3d( 0.0, 0.0, 0.0 );
        glVertex3d( 0.0, length, 0.0 );
        // z-axis.
        glColor3f( 0.0, 0.0, 1.0 );
        glVertex3d( 0.0, 0.0, 0.0 );
        glVertex3d( 0.0, 0.0, length );
    glEnd();
    glPopAttrib();
}

void VertexBufferGL::render() {
	// draw a test triangle
	/*
	glBegin(GL_TRIANGLES);
		glColor3f(1.0f, 0.0f, 0.0f);
		glVertex3f(0.0f, 0.0f, 0.0f);
		glColor3f(0.0f, 1.0f, 0.0f);
		glVertex3f(24.0f, 0.0f, 0.0f);
		glColor3f(0.0f, 0.0f, 1.0f);
		glVertex3f(0.0f, 0.0f, 24.0f);
	glEnd();
	*/

	// draw axis
	Camera* c = Renzoderer::instance()->getActiveScene()->getActiveCamera();
	DrawAxes(c->getFarPlane());
	

	//
	// ===== State management =====
	//

	//
	// Texture mapping
	//
	glEnable(GL_TEXTURE_2D);
	
	//
	// Depth test
	//
	glEnable(GL_DEPTH_TEST);

	// 
	// Backface culling
	// Renzoderer's convention: front face is CCW. The same as OpenGL.
	//
	glFrontFace(GL_CCW); 
	glEnable(GL_CULL_FACE);

	//
	// Anti-aliasing
	//
	/*
	glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
	glEnable(GL_LINE_SMOOTH);
	glEnable(GL_POLYGON_SMOOTH);
	glEnable (GL_BLEND);
	//glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glBlendFunc (GL_SRC_ALPHA_SATURATE, GL_ONE);
	*/
	glEnable(GL_MULTISAMPLE_ARB);

	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	// http://www.songho.ca/opengl/gl_vbo.html	
	glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo);
	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ibo);	
	
	glEnableClientState(GL_VERTEX_ARRAY);                 // activate vertex coords array
	
	//glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), BUFFER_OFFSET(0));
	// 3rd parameter (stride) explanation
	// Strides specify how elements are arranged in the array. Stride = 0 means the array is tightly packed.	
	
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glClientActiveTexture(GL_TEXTURE0);
	// TODO: cannot pass 4 float (s, t, r, q) ... why?
	
	

	// TODO: how to bind multiple textures here? -> make a lot of draw calls
	for (int i = 0; i < objectOffsets.size(); ++i) {
		int vertexBufferOffset = (objectOffsets[i].vertexOffset.start) * sizeof(Vertex);
		glVertexPointer(3, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(vertexBufferOffset));
		glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(vertexBufferOffset + 40));

		Texture* t = textures[objectOffsets[i].textureOffset.start];
		Renzoderer::instance()->getTextureManager()->bindTexture(t);
		
		int nbIndices = objectOffsets[i].indexOffset.end - objectOffsets[i].indexOffset.start + 1;
		int indexBufferOffset = (objectOffsets[i].indexOffset.start) * sizeof(Index);
		glDrawElements(GL_TRIANGLES, nbIndices, GL_UNSIGNED_INT, BUFFER_OFFSET(indexBufferOffset));
		//indexBufferOffset += nbIndices * sizeof(unsigned int);
	}

	//glDrawElements(GL_TRIANGLES, 3 * nbTriangles, GL_UNSIGNED_INT, BUFFER_OFFSET(0));

	//glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), BUFFER_OFFSET(0));
	//glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, BUFFER_OFFSET(0));

	glDisableClientState(GL_VERTEX_ARRAY);			// disable vertex arrays
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);	// disable texture arrays

	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
	glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
	
	
}

}